﻿using System.Collections.Generic;
using Server.Global;
using The_World_Server;
using Server.Network;
using Server.Manager;
using TheWorldLibrary.Characters;
using Microsoft.Xna.Framework;
using TheWorldLibrary.Sprites;
using Server.Maps;

namespace Server.PC
{
    public class Player
    {
        public List<Character> Players = new List<Character>();
        public List<string> AccountName = new List<string>();

        #region Constructor
        // private constructor
        private Player() { }

        // singleton accessor
        static Player _instance;
        public static Player Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new Player();
                return _instance;
            }
        }

        #endregion

        #region Players

        public void JoinGame(int index)
        {
            // See if player is already in game
            if (Players[index].Entity.InGame)
            {
                return;
            }

            // Set player as in game.
            Players[index].Entity.InGame = true;

            // Update total players
            Static.TotalPlayersOnline++;

            // Update the log
            // TODO: Update the log to show who is connected.

            // Warp player to saved map

            // Send the okay to be in game on client
            ServerTcp.Instance.SendJoinedGame(index);

            // Send Welcome Message
            if (Static.TotalPlayersOnline - 1 > 0)
            {
                ServerTcp.Instance.SendPlayerMsg(index, "There are currently " + Static.TotalPlayersOnline + " players online.");
            }
            else
            {
                ServerTcp.Instance.SendPlayerMsg(index, "There is no one online.");
            }

            ServerTcp.Instance.SendMapMsg(index, GetPlayerName(index) + " has logged in.");

            // Show on server someone logged in
            Program.server.TextAdd(GetPlayerName(index) + " has logged into the game");

            // Test Player warp
            PlayerWarp(index, 0, 5, 5);
        }

        public void LeftGame(int index)
        {
            if (index < 0 || Players[index] == null)
            {
                return;
            }

            if (Players[index].Entity.InGame)
            {
                // Update total Player
                Static.TotalPlayersOnline--;

                // Update the log
                // TODO: Update the log to show player has left.

                // Save player
                DataManager.Instance.SavePlayer(Players[index].Entity.EntityName, index);

                // Let other know he left
                ServerTcp.Instance.SendMapMsg(index, GetPlayerName(index) + " has left " + Constants.GAME_NAME);

                Program.server.TextAdd(GetPlayerName(index) + " has left the game.");

                // Clear Player - update clients//
                Players[index].Entity.InGame = false;
                DataManager.Instance.SavePlayer(Players[index].Entity.EntityName, index);
                ClearPlayer(index);
                ServerTcp.Instance.SendLeftGame(index);
            }
        }

        public void ClearAllPlayers()
        {
            for (int i = 1; i <= Constants.MAX_PLAYERS - 1; i++)
            {
                ClearPlayer(i);
            }
        }

        public void ClearPlayer(int index)
        {
            Players[index] = null;
        }

        #endregion

        #region Player Movement/Warp

        public void PlayerMove(int index, int mapNum, int dir, float x, float y)
        {
            int newMapX = 0, newMapY = 0;
            var newPos = new Vector2(x, y);

            var isPlaying = ServerTcp.Instance.IsPlaying(index);

            // Check for out of range
            if (isPlaying == false || dir < (int)AnimationKey.Down || dir > (int)AnimationKey.Up)
            {
                // Error so stop the movement;
                return;
            }

            #region Boundries

            // East
            if ((int)newPos.X / 32 > MapManager.Instance.Worlds.Levels[mapNum].MaxWidth)
            {
                // Out of bounds keep them at the old position. So just exit out.
                return;
            }

            // West
            if ((int)newPos.X / 32 < 0)
            {
                // Out of bounds keep them at the old position. So just exit out.
                return;
            }
            
            // South
            if ((int)newPos.Y / 32 > MapManager.Instance.Worlds.Levels[mapNum].MaxHeight)
            {
                // Out of bounds keep them at the old position. So just exit out.
                return;
            }

            // North
            if ((int)newPos.Y / 32 < 0)
            {
                // Out of bounds keep them at the old position. So just exit out.
                return;
            }

            #endregion

            #region Tile Properties
            if (MapManager.Instance.Worlds.Levels[MapManager.Instance.Worlds.CurrentLevel].Map.blockedTile[(int) newPos.Y * MapManager.Instance.Worlds.Levels[MapManager.Instance.Worlds.CurrentLevel].MaxWidth + (int)newPos.X] != null)
            {
                // tile is blocked Can't move.
                return;
            }
            

            #endregion

            #region NPC/Players



            #endregion

            // Everything is okay at this point. Update the player position and send it to the everyone on that map.
            #region Update Player

            // Update players position
            Players[index].Sprite.Position = new Vector2(x, y);

            // Change players direction
            Players[index].Sprite.CurrentAnimation = (AnimationKey)dir;

            #endregion
        }

        public void PlayerWarp(int index, int mapNum, int x, int y)
        {
            int oldMap;

            // Check to make sure index is actually a player or map is actual map.
            if ((!ServerTcp.Instance.IsPlaying(index)) || (mapNum < 0) || (mapNum > Constants.MAX_MAPS))
            {
                return;
            }

            // Check the bounds of themap
            // TODO: Add in checking for max X and max Y both greater and less than

            // Save the old map of the player
            oldMap = GetPlayerMap(index);

            // I will be adding in LeaveMap and JoinMap next release
            // TODO: SendLeaveMap(index, mapNum);

            Players[index].Entity.EntityMap = mapNum;
            Players[index].Sprite.Position = new Vector2(x * 32, y * 32);

            //Update players position
            ServerTcp.Instance.SendPlayerNewXY(index);

            // Send the new information to the everyone.
            ServerTcp.Instance.SendPlayerData(index);
        }

        #endregion

        #region Player Commands

        // Get Player Name
        public string GetPlayerName(int index)
        {
            return Players[index].Entity.EntityName;
        }

        // Retrive Player Access
        public byte GetPlayerAccess(int index)
        {
            // TODO: Add in player access ranks.
            return 0;
        }

        // Retrieve player Sprite
        public int GetPlayerSprite(int index)
        {
            return Players[index].Entity.EntitySpriteNum;
        }

        // Retrive Player Map
        public int GetPlayerMap(int index)
        {
            return Players[index].Entity.EntityMap;
        }

        //Retirve Player X
        public int GetPlayerX(int index)
        {
            return (int)Players[index].Sprite.Position.X;
        }

        // Retreive Player Y
        public int GetPlayerY(int index)
        {
            return (int)Players[index].Sprite.Position.Y;
        }

        // Get Player Level
        public int GetPlayerLevel(int index)
        {
            // TODO: Add in leveling system
            return 0;
        }

        // Set Player Level
        public bool SetPlayerLevel(int index, int level)
        {
            // TODO: Add in leveling System
            return false;
        }

        // Retrieve Player Points
        public int GetPlayerPoints(int index)
        {
            // TODO: Add in player point system.
            return 0;
        }

        // Set Player Points
        public void SetPlayerPoints(int index, int points)
        {
            // TODO: Add in player point system.
        }

        // Get Player Stat
        public int GetPlayerStat(int index, int stat)
        {
            switch (stat)
            {
                case 1: // Strength
                    return Players[index].Entity.Strength;
                case 2: // Dexterity
                    return Players[index].Entity.Dexterity;
                case 3: // Cunning;
                    return Players[index].Entity.Cunning;
                case 4: // Willpower
                    return Players[index].Entity.Willpower;
                case 5: // Magic
                    return Players[index].Entity.Magic;
                case 6: // Constitution
                    return Players[index].Entity.Constitution;
            }
            return 0;
        }

        // GetPlayer Vital
        public int GetPlayerVital(int index, int vital)
        {
            switch (vital)
            {
                case 1: // Health 
                    return Players[index].Entity.Health.CurrentValue;
                case 2: // Stamina
                    return Players[index].Entity.Stamina.CurrentValue;
                case 3: // Mana
                    return Players[index].Entity.Mana.CurrentValue;
            }
            return 0;
        }

        // Set PLayer Vital
        public void SetPlayerVital(int index, int vital, int amount)
        {
            switch (vital)
            {
                case 1: // Health
                    Players[index].Entity.Health.SetCurrent(amount);
                    break;
                case 2: // Stamina
                    Players[index].Entity.Stamina.SetCurrent(amount);
                    break;
                case 3: // Mana
                    Players[index].Entity.Mana.SetCurrent(amount);
                    break;
            }
        }

        // Get Player Max Vital
        public int GetPlayerMaxVital(int index, int vital)
        {
            switch (vital)
            {
                case 1: // Health 
                    return Players[index].Entity.Health.MaximumValue;
                case 2: // Stamina
                    return Players[index].Entity.Stamina.MaximumValue;
                case 3: // Mana
                    return Players[index].Entity.Mana.MaximumValue;
            }
            return 0;
        }

        // Retrieve Player Experience
        public int GetPlayerExp(int index)
        {
            // TODO: Add in Experience system
            return 0;
        }

        // Set Player Experience
        public void SetPlayerExp(int index, int amount)
        {
            // TODO: Add in Experience System.
        }

        // Retrieve Player Max Exp
        public int GetPlayerMaxExp(int index)
        {
            return 100 + (((GetPlayerLevel(index) ^ 2) * 10) * 2);
        }

        // Give Player Experience
        public void GiveExp(int index, int amount)
        {
            // TODO: Add in Experience system.
        }

        #endregion
    }
}
